package com.itisyue.fun.controller;

import com.itisyue.fun.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.CompletableFuture;

import static java.nio.file.StandardOpenOption.READ;

@Slf4j
@RestController
public class FileControllerV0 {
    @Autowired
    private FileService fileService;

    @Value("${file.base-dir:/}")
    private String baseDir;
    
    @GetMapping("/fileV0")
    public CompletableFuture<ResponseEntity<Resource>> getFile(
            @RequestParam String filePath) {
        log.info("filePath={}",filePath);
        return fileService.readFileAsync(filePath)
                .thenApply(resource -> 
                    ResponseEntity.ok()
                        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""+filePath+"\"")
                        .header(HttpHeaders.CONTENT_TYPE, "application/octet-stream")
                        .body(resource)
                );
    }

    @GetMapping("/fileV01")
    public ResponseEntity<StreamingResponseBody> downloadFile(@RequestParam String filePath) {
        Path path = Paths.get(baseDir, filePath).normalize();

        // 流式响应实现
        StreamingResponseBody body = outputStream -> {
            try (InputStream is = Files.newInputStream(path)) {
                byte[] buffer = new byte[4 * 1024]; // 4KB缓冲区
                int bytesRead;
                while ((bytesRead = is.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                    outputStream.flush(); // 确保实时传输
                }
            }
        };

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""+path.getFileName()+"\"")
                .body(body);
    }

    @GetMapping("/fileV02")
    public ResponseEntity<StreamingResponseBody> downloadFileV2(
            @RequestParam String filePath) throws IOException {

        Path path = Paths.get(baseDir, filePath).normalize();

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""+path.getFileName()+"\"")
                .body(outputStream -> {
                    try (FileChannel channel = FileChannel.open(path, READ)) {  // ✅ 自动关闭
                        WritableByteChannel outChannel = Channels.newChannel(outputStream);
                        channel.transferTo(0, channel.size(), outChannel);  // 零拷贝传输[1,8](@ref)
                    }
                });
    }

    @GetMapping("/fileV03")
    public CompletableFuture<ResponseEntity<StreamingResponseBody>> downloadFileV3(
            @RequestParam String filePath) {

        return fileService.validatePathAsync(filePath)
                .thenApply(path -> {
                    StreamingResponseBody body = outputStream -> {
                        try (FileChannel channel = FileChannel.open(path, READ)) {
                            channel.transferTo(0, channel.size(), Channels.newChannel(outputStream));
                        }
                    };

                    return ResponseEntity.ok()
                            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\""+path.getFileName()+"\"")
                            .body(body);
                })
                .exceptionally(e -> ResponseEntity.status(404).build());
    }

    @GetMapping("/fileV04")
    public void downloadFileV4(@RequestParam String filePath, HttpServletResponse response) throws IOException{
        long startTime = System.currentTimeMillis();

        log.info("filePath={}",(baseDir + filePath));
        File file = ResourceUtils.getFile(baseDir + filePath);
        String fileName = URLEncoder.encode(file.getName(), "UTF-8");
        log.info("fileName={}", fileName);
        response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
        response.setContentType("application/octet-stream");
        //循环，边读取边输出，可避免大文件时OOM
        try(InputStream inputStream = new FileInputStream(file); OutputStream os = response.getOutputStream()){
            byte[] bytes = new byte[1024];
            int readLength;
            while ((readLength = inputStream.read(bytes)) != -1){
                os.write(bytes, 0, readLength);
            }
        }
        long endTime = System.currentTimeMillis();
        log.info("timeCost={}ms", (endTime-startTime));
    }
}